///所有权
////所有权有以下三条规则：
/// Rust 中的每个值都有一个变量，称为其所有者。
/// 一次只能有一个所有者。
/// 当所有者不在程序运行范围时，该值将被删除。

pub fn ownership_demo1(){
    {
        // 在声明以前，变量 s 无效
        let s = "runoob";
        // 这里是变量 s 的可用范围
    }
// 变量范围已经结束，变量 s 无效

}

///变量与数据交互的方式
///变量与数据交互方式主要有移动（Move）和克隆（Clone）两种：
//
// 移动
// 多个变量可以在 Rust 中以不同的方式与相同的数据交互：
//
// let x = 5;
// let y = x;
//这个程序将值 5 绑定到变量 x，然后将 x 的值复制并赋值给变量 y。现在栈中将有两个值 5。此情况中的数据是"基本数据"类型的数据，
// 不需要存储到堆中，仅在栈中的数据的"移动"方式是直接复制，这不会花费更长的时间或更多的存储空间。"基本数据"类型有这些：
//
// 所有整数类型，例如 i32 、 u32 、 i64 等。
// 布尔类型 bool，值为 true 或 false 。
// 所有浮点类型，f32 和 f64。
// 字符类型 char。
// 仅包含以上类型数据的元组（Tuples）。

//但如果发生交互的数据在堆中就是另外一种情况：

//let s1 = String::from("hello"); //产生一个 String 对象，值为 "hello"。其中 "hello" 可以认为是类似于长度不确定的数据，需要在堆中存储。
//let s2 = s1; //为了确保安全，在给 s2 赋值时 s1 已经无效了。没错，在把 s1 的值赋给 s2 以后 s1 将不可以再被使用。


// 克隆
//Rust会尽可能地降低程序的运行成本，所以默认情况下，长度较大的数据存放在堆中，且采用移动的方式进行数据交互。
// 但如果需要将数据单纯的复制一份以供他用，可以使用数据的第二种交互方式——克隆。

pub fn clone_demo(){
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("s1 = {}, s2 = {}", s1, s2); //s1 = hello, s2 = hello
    //这里是真的将堆中的 "hello" 复制了一份，所以 s1 和 s2 都分别绑定了一个值，释放的时候也会被当作两个资源。
    //
    // 当然，克隆仅在需要复制的情况下使用，毕竟复制数据会花费更多的时间。
}

///涉及函数的所有权机制

//对于变量来说这是最复杂的情况了。
//
// 如果将一个变量当作函数的参数传给其他函数，怎样安全的处理所有权呢？
//
// 下面这段程序描述了这种情况下所有权机制的运行原理：

pub fn ownership_demo2(){
    let s = String::from("hello");
    // s 被声明有效

    takes_ownership(s);
    // s 的值被当作参数传入函数
    // 所以可以当作 s 已经被移动，从这里开始已经无效

    let x = 5;
    // x 被声明有效

    makes_copy(x);
    // x 的值被当作参数传入函数
    // 但 x 是基本类型，依然有效
    // 在这里依然可以使用 x 却不能使用 s

}// 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放

fn takes_ownership(some_string: String) {
    // 一个 String 参数 some_string 传入，有效
    println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放

fn makes_copy(some_integer: i32) {
    // 一个 i32 参数 some_integer 传入，有效
    println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放

///函数返回值的所有权机制

pub fn ownership_demo3(){
    let s1 = gives_ownership();
    // gives_ownership 移动它的返回值到 s1

    let s2 = String::from("hello");
    // s2 被声明有效

    let s3 = takes_and_gives_back(s2);
    // s2 被当作参数移动, s3 获得返回值所有权
}// s3 无效被释放, s2 被移动, s1 无效被释放.

fn gives_ownership() -> String {
    let some_string = String::from("hello");
    // some_string 被声明有效

    return some_string;
    // some_string 被当作返回值移动出函数
}
fn takes_and_gives_back(a_string: String) -> String {
    // a_string 被声明有效

    a_string  // a_string 被当作返回值移出函数
    //被当作函数返回值的变量所有权将会被移动出函数并返回到调用函数的地方，而不会直接被无效释放。
}

/// 引用与租借
//& 运算符可以取变量的"引用"

//当一个变量的值被引用时，变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值
//引用不会获得值的所有权。
//引用只能租借（Borrow）值的所有权。
//引用本身也是一个类型并具有一个值，这个值记录的是别的值所在的位置，但引用不具有所指值的所有权
pub fn ownership_demo4(){

    let s1 = String::from("hello");
    let s2 = &s1;
    println!("s1 is {}, s2 is {}", s1, s2); //s1 is hello, s2 is hello
}

//如果尝试利用租借来的权利来修改数据会被阻止
pub fn ownership_demo5(){

    let s1 = String::from("run");
    let s2 = &s1;
    println!("{}", s2);
    //s2.push_str("oob"); // 错误，禁止修改租借的值
    println!("{}", s2);
}

//当然，也存在一种可变的租借方式，就像你租一个房子，如果物业规定房主可以修改房子结构，
// 房主在租借时也在合同中声明赋予你这种权利，你是可以重新装修房子的：
//用 &mut 修饰可变的引用类型
pub fn ownership_demo6(){

    let mut s1 = String::from("run");
    // s1 是可变的

    let s2 = &mut s1;
    // s2 是可变的引用

    s2.push_str("oob");
    println!("{}", s2);
}

//可变引用与不可变引用相比除了权限不同以外，可变引用不允许多重引用，但不可变引用可以
//这段程序不正确，因为多重可变引用了 s。
pub fn ownership_demo7(){

    let mut s = String::from("hello");

    let r1 = &mut s;
    //let r2 = &mut s; //cannot borrow `s` as mutable more than once at a time

    //println!("{}, {}", r1, r2);
}

//垂悬引用（Dangling References）
//"垂悬引用"在 Rust 语言里不允许出现，如果有，编译器会发现它。
pub fn ownership_demo8(){
    // let reference_to_nothing = dangle();
}
//很显然，伴随着 dangle 函数的结束，其局部变量的值本身没有被当作返回值，被释放了。但它的引用却被返回，
// 这个引用所指向的值已经不能确定的存在，故不允许其出现。

/*fn dangle() -> &String {
    let s = String::from("hello");
    &s
}
*/
#[test]
pub fn ownership_demo9(){
    let mut x = 30;
    let r = &mut x;
    *r = 90;
    x = 80;
    let y = x;
    println!("{}, {}", y, x);

    let mut s = String::new();
    let rs = &mut s;
    //s.push('c'); //不能同时和rs一起用
    rs.push('4');
    println!("{}", s);
}